#include "../include/crash_handler.h"
#include "../include/event.h"
#include "../include/global_settings.h"
#include "../include/default_handler.h"
#include "../include/object.h"
#include <math.h>
#include <stdlib.h>

int evt_break_id;
int evt_objemerge_id;
int evt_randombox_active_id;
int evt_automotion_ctrl_id;
int evt_automotion_ctrlB_id;
int evt_bulletvanish_id;

static bool relativelySolid(hObject obj1, hObject obj2)
{
	pObject pobj1 = getData(obj1), pobj2 = getData(obj2);
	if(pobj1->solid == SOLID && pobj2->solid == SOLID)return true;
	if(pobj1->solid == HALF_SOLID && pobj2->solid == SOLID)
	{
		pObject temp = pobj1;
		pobj1 = pobj2;
		pobj2 = temp;
	}
	if(pobj1->solid == SOLID && pobj2->solid == HALF_SOLID)
	{
		if(pobj2->pos.y + pobj2->size.y - pobj1->pos.y <= ACCEPTABLE_DIST
			&& pobj2->pos.y +pobj2->size.y - pobj1->pos.y >= -PRECISION
			&& pobj1->speed.y <= -PRECISION)return true;
	}
	return false;
}

void ehAutoMotionCtrl(pEventInfo info)
{
	if(!findObject(info->obj1))return;

	pObject pobj = getData(info->obj1);
	if(pobj->speed.x > 0)pobj->speed.x = MAX_SPEED_X / 1.5f;
	else
		if(pobj->speed.x < 0)pobj->speed.x = -MAX_SPEED_X / 1.5f;

	info->time_interval = 1;
}

void ehAutoMotionCtrlB(pEventInfo info)
{
	if(!findObject(info->obj1))return;
    pObject pobj = getData(info->obj1);
	if(pobj->speed.x > 0)pobj->speed.x = MAX_SPEED_X;
	else
		if(pobj->speed.x < 0)pobj->speed.x = -MAX_SPEED_X;
	if(info->param1 >= 3)
    {
        pobj->img_index = (pobj->img_index /4)*4 + (pobj->img_index +1)%4;
		info->param1 = 0;
    }
    else
		info->param1++;
	info->time_interval = 1;
}
void ehBulletVanish(pEventInfo info)
{
	removeObject(info->obj1);
}

void ehFragileBlockBreak(pEventInfo info)
{
	if(!findObject(info->obj1))return;

	pObject pobj = getData(info->obj1);
	if(info->param1 <= 3)
	{
		pobj->img_index = info->param1;

		float cent_x = pobj->pos.x + pobj->size.x / 2, cent_y = pobj->pos.y + pobj->size.y / 2 + 0.1f / info->param1;
		pobj->size.x = pobj->size.x / info->param1 * (info->param1 + 1);
		pobj->size.y = pobj->size.y / info->param1 * (info->param1 + 1);
		pobj->pos.x = cent_x - pobj->size.x / 2;
		pobj->pos.y = cent_y - pobj->size.y / 2;
	}
	else
	{
		pobj->pos.y -= 0.01f * info->param1 * info->param1;
	}

	info->param1++;

	if(info->param1 <=10)info->time_interval = 4;
	else
		removeObject(info->obj1);
}

void fragileBlockBreak(hObject fragile_block)
{
	pObject pobj = getData(fragile_block);
	pobj->solid = 0;

	EventInfo info;
	info.event_type = evt_break_id;
	info.obj1 = fragile_block;
	info.param1 = 1;
	info.time_interval = 4;
	addEvent(&info, EVT_NORMAL_PRIORITY);
}

void ehObjEmerge(pEventInfo info)
{
	if(!findObject(info->obj1))return;
	float displacement = 0.02f;
	pObject pobj = getData(info->obj1);
	pobj->pos.y += displacement;
	info->param1++;
	if(info->param1 == (int)floor(pobj->size.y / displacement))
	{
		if(pobj->obj_type == TAG)
		{
			removeObject(info->obj1);
		}
		else
		{
			pobj->solid = SOLID;
			EventInfo obj_move = {info->obj1, 0, evt_motion_id, 1};
			addEvent(&obj_move, EVT_MAX_PRIORITY);
			EventInfo obj_move_ctrl = {info->obj1, 0, evt_automotion_ctrl_id, 1};
			addEvent(&obj_move_ctrl, EVT_NORMAL_PRIORITY);
		}
	}
	else
		info->time_interval = 5;
}

void ehRandomBoxActive(pEventInfo info)
{
	if(!findObject(info->obj1))return;

	pObject pobj = getData(info->obj1);
	removeObject(info->obj2);
	pobj->img_index = 0;
}

void randomBoxAction(hObject randombox)
{
	pObject pobj = getData(randombox);
    int result = rand() % 4;
    Object new_obj = {0, 0, 0, 0, {pobj->pos.x, pobj->pos.y}, {0.1f, 0.1f}, 
								0, 0, 0, 0, {(rand() % 2 ? 1 : -1) * MAX_SPEED_X, 0}};
    hObject hnew_obj = 0;
	pobj->img_index = 1;
	switch(result)
	{
	case 0:
		new_obj.obj_type = FLOWER;
		new_obj.fixed = 1;
        break;
    case 1:
		new_obj.obj_type = MUSHROOM2;
		break;
	case 2:
		new_obj.obj_type = POISONED_MUSHROOM;
		break;
	case 3:
		new_obj.obj_type = TAG;
		new_obj.img_index = 0;
		break;
	}

	hnew_obj = newObject(&new_obj);
	addObject(hnew_obj);    
    EventInfo objemerge = {hnew_obj, 0, evt_objemerge_id, 5, 0};
    addEvent(&objemerge, EVT_NORMAL_PRIORITY);
    EventInfo randombox_active = {randombox, hnew_obj, evt_randombox_active_id, 700};
    addEvent(&randombox_active, EVT_NORMAL_PRIORITY);

}

void stomp(hObject obj1, hObject obj2)
{
	pObject pobj1 = getData(obj1), pobj2 = getData(obj2);

	if(!relativelySolid(obj1, obj2))return;

    if(pobj1->alive && pobj2->obj_type == POISONED_MUSHROOM)
	{
		removeObject(obj2);
		for(int i = 0; i < getPlayerCount(); i++)
			if(i != getMarioId(obj1))Die(getPlayer(i));
	}
	else if(pobj2->alive && pobj1->obj_type == POISONED_MUSHROOM)
	{
		removeObject(obj1);
		for(int i = 0; i < getPlayerCount(); i++)
			if(i != getMarioId(obj2))Die(getPlayer(i));
	}
	else if(pobj1->alive && pobj2->obj_type == MUSHROOM2 )
	{
		removeObject(obj2);
		pobj1->hp += 2;
	}
	else if(pobj2->alive && pobj1->obj_type == MUSHROOM2)
	{
		removeObject(obj1);
		pobj2->hp += 2;
	}
	else if(pobj1->alive && pobj2->obj_type == FLOWER )
	{
		removeObject(obj2);
		pobj1->weapon++;
	}
	else if(pobj2->alive && pobj1->obj_type == FLOWER)
	{
		removeObject(obj1);
		pobj2->weapon++;
	}
    else if(pobj1->alive && pobj2->obj_type == BULLET && pobj2->coin != getMarioId(obj1))
	{
		removeObject(obj2);
		Die(obj1);
	}
	else if(pobj2->alive && pobj1->obj_type == BULLET && pobj1->coin != getMarioId(obj2))
	{
		removeObject(obj1);
		Die(obj2);
	}
    else if(pobj1->alive && pobj2->alive)
	{

		Die(obj2);
		pobj1->speed.y = (float)fabs(pobj1->speed.y) * ELASTIC_COEFFICIENT_Y;
	}
	else
	{
		if(pobj2->alive && pobj2->speed.y >= PRECISION)
		{
			if(pobj1->obj_type == FRAGILE_BLOCK)fragileBlockBreak(obj1);
			else if(pobj1->obj_type == RANDOM_BOX && pobj1->img_index == 0)randomBoxAction(obj1);
		}
		if(pobj1->obj_type != BULLET)pobj1->speed.y = 0;
		else
            pobj1->speed.y = MAX_SPEED_Y/ 2.0;
		pobj2->speed.y = 0;
	}

	pobj1->acc.y = 0.0f;
	if(fabs(pobj1->speed.x) >= ACC_X)pobj1->acc.x += (pobj1->speed.x <= 0) ? ACC_X : -ACC_X;
	else
		pobj1->acc.x = 0;
}

void crash(hObject obj1, hObject obj2)
{
	pObject pobj1 = getData(obj1), pobj2 = getData(obj2);
	float temp;

	if(!relativelySolid(obj1, obj2))return;

	//should be rewritten into several functions
	if(pobj1->alive && pobj2->obj_type == POISONED_MUSHROOM)
	{
		removeObject(obj2);
		for(int i = 0; i < getPlayerCount(); i++)
			if(i != getMarioId(obj1))Die(getPlayer(i));
	}
	else if(pobj2->alive && pobj1->obj_type == POISONED_MUSHROOM)
	{
		removeObject(obj1);
		for(int i = 0; i < getPlayerCount(); i++)
			if(i != getMarioId(obj2))Die(getPlayer(i));
	}
	else if(pobj1->alive && pobj2->obj_type == MUSHROOM2)
	{
		removeObject(obj2);
		pobj1->hp += 2;
	}
	else if(pobj2->alive && pobj1->obj_type == MUSHROOM2)
	{
		removeObject(obj1);
		pobj2->hp += 2;
	}
	else if(pobj1->alive && pobj2->obj_type == FLOWER )
	{
		removeObject(obj2);
		pobj1->weapon++;
	}
	else if(pobj2->alive && pobj1->obj_type == FLOWER)
	{
		removeObject(obj1);
		pobj2->weapon++;
	}
	else if(pobj1->alive && pobj2->obj_type == BULLET && pobj2->coin != getMarioId(obj1))
	{
		removeObject(obj2);
		Die(obj1);
	}
	else if(pobj2->alive && pobj1->obj_type == BULLET && pobj1->coin != getMarioId(obj2))
	{
		removeObject(obj1);
		Die(obj2);
	}
	else
	{
		if(pobj1->fixed && !pobj2->fixed)
		{
			pobj2->speed.x = -pobj2->speed.x / 2 * ELASTIC_COEFFICIENT_X;
		}
		else if(pobj2->fixed && !pobj1->fixed)
		{
			pobj1->speed.x = -pobj1->speed.x / 2 * ELASTIC_COEFFICIENT_X;
		}
		else
		{
			//cout << pobj1->speed.x << ' ' << pobj2->speed.x << endl;

			temp = pobj1->speed.x;
			pobj1->speed.x = pobj2->speed.x * ELASTIC_COEFFICIENT_X;
			pobj2->speed.x = temp * ELASTIC_COEFFICIENT_X;

			//cout << pobj1->speed.x << ' ' << pobj2->speed.x << endl;
			//cout << pobj1->acc.x << ' ' << pobj2->acc.x << endl;
		}
	}
}

extern "C" void crashHandlerInit()
{
	evt_break_id = registerEventType(ehFragileBlockBreak);
	evt_objemerge_id = registerEventType(ehObjEmerge);
	evt_randombox_active_id = registerEventType(ehRandomBoxActive);
	evt_automotion_ctrl_id = registerEventType(ehAutoMotionCtrl);
	evt_automotion_ctrlB_id = registerEventType(ehAutoMotionCtrlB);
	evt_bulletvanish_id = registerEventType(ehBulletVanish);
}
